import cherrypy
import time
import logging
from logst.logs_abstraction import *

class webui_root:

   def exception(self):

      raise "Fake_Exception"

   def _cp_on_error(self):

      logger = logging.getLogger("logpox")

      bt = traceback.format_exc()

      logger.info("""error on page, traceback follows:\n%s""" % bt)

      cherrypy.response.body = """%s

<h2>An exception occurred:</h2>

<PRE>%s</PRE>

<P>
<h3>Please report this issue by sending the traceback above and possibly the data utilized in this session.</h3>

%s""" % ( self.header(), bt, self.footer() )

   def header(self, meta_redirect = False, body_onload = False):

      fp = open(self.CONFIG["sharedir"] + "/html/header.html", "r")

      out = fp.read()
      fp.close()

      if meta_redirect:
         out = re.sub(r"""(\<head.*\>)""", """\\1""" + """<META HTTP-EQUIV="Refresh" CONTENT="%d; URL=%s">""" % (meta_redirect[0], meta_redirect[1]), out)

      if body_onload:
         out = re.sub(r"""(\<body)(.*)(\>)""", """\\1 \\2 onload="%s"\\3""" % (body_onload), out)

      return out

   def footer(self):
      fp = open(self.CONFIG["sharedir"] + "/html/footer.html", "r")
      toret = fp.read()
      fp.close()
      return toret

   def link_to_log(self, log):
      return """/browse?date=%s#%s_%s""" % (log.timestamp(), log.hostname(), log.position)

   def debug(self):
      pdb.set_trace()

   def rpc_event_logs(self, event_id):

      event_id = int(event_id)

      cluster = self.cluster_master

      event = cluster.events.get(event_id)

      ltext = event.get_long_text()
      if len(ltext) > 0:

         yield """<div style="padding: 20px; font-size: larger">%s</div>\n""" % ltext

      yield html_print_logs(event.logs)

   def rpc_star_log(self, log_id):

      cluster = self.cluster_master

      log = cluster.get_log_by_id(log_id)

      img = if_true( log.toggle_starred(), "32px-Crystal_Clear_action_bookmark.png", "32px-Crystal_Clear_action_bookmark_Silver_half.png" )

      return "%s|/static/images/%s" % (log.id(), img)

   def parse(self):

      cluster = self.cluster_master

      if not cluster.is_parsing():

         cluster.parse_hyper(threaded = True)

      raise cherrypy.HTTPRedirect('/parsing_in_progress')

   def parse_advanced(self, **xargs):

      cluster = self.cluster_master

      if xargs:

         for parser in cluster.list_parsers():

            if parser.is_disabled() and xargs.has_key(parser.name()):
               parser.enabled = True
               continue

            elif parser.is_enabled() and not xargs.has_key(parser.name()):
               parser.enabled = False

            for option in parser.list_options():

               if option.is_boolean():

                  if option.uid in xargs.keys():
                     if option.is_false():
                        option.set_value(xargs[option.uid])
                  else:
                     option.set_value(False)

               else:
                  option.set_value(xargs[option.uid])

            parser.reinit_activators()

         cluster.store_parsers_options()

         if xargs.has_key("do_parse"):
            raise cherrypy.HTTPRedirect('/parse')

      yield self.header()

      yield """<h3>The following options are available for your parsers:</h3>"""

      yield """<form method="post">"""

      for parser in cluster.list_parsers():

         if parser.has_options():
            yield """<div style="margin: 30px">"""
         else:
            yield """<div style="color: #bababa; margin: 30px">"""

         yield """<div><img style="vertical-align: middle; padding-right: 10px;" src="/static/%s"><input type="checkbox" %s name="%s" onchange="javascript:this.form.submit();">%s<span style="padding-left: 20px; font-size: smaller; color: green">%s</span></div>""" % ( parser.default_icon, if_true(parser.is_enabled(), "checked"), parser.name(), parser.name(), parser.get_description() )

         yield """<div style="padding-left: 100px">"""

         if parser.is_disabled():
            yield "This parser is disabled."

         elif not parser.has_options():
            yield "No options available."

         else:

            yield """<table>"""

            inc = 0

            for option in parser.list_options():

               if inc % 3 == 0:
                  yield """<tr><td>"""

               yield "<td>"

               if option.is_boolean():

                  yield """<input type="checkbox" name="%s" %s>""" % ( option.uid, if_true(option.is_true(), "checked") )

               else:

                  yield """<select name="%s">""" % option.id
                  for value in option.possible_values:
                     yield """<option %s>%s""" % ( if_true(value == option.value, "selected"), value )
                  yield """</select>\n<br>"""

               yield option.description

               inc += 1

            yield "</table>"

         yield "</div></div>"

      yield """<div style="position: absolute; right: 100px"><input type="submit" name="do_parse" value="Begin parsing..."></div></form>"""

      yield self.footer()

   def parsing_in_progress(self):

      cluster = self.cluster_master

      if not cluster.is_parsing():
         if cluster.has_parsed():
            raise cherrypy.HTTPRedirect('/events')
         else:
            raise cherrypy.HTTPRedirect('/parse_advanced')

      yield self.header( body_onload = "javascript:setInterval(rpc_parsing_redirect, 2500);" )

      yield """<h3><img style="padding-right: 10px" src="/static/images/loading.gif" alt="Parsing in progress...">Parsing in progress.</h3>\nThis page will refresh automatically, results will be displayed when parsing will have ended.<p>If you are in a hurry, in the meanwhile you can begin <a href="/browse">browsing the logs</a>."""

      yield self.footer()

   def rpc_parsing_redirect(self):

      if self.cluster_master.has_parsed():
         return "/events"
      elif self.cluster_master.is_parsing():
         time.sleep(1)
         return ""
      else:
         return "/parse"

   def events(self):

      cluster = self.cluster_master

      if cluster.is_parsing():
         raise cherrypy.HTTPRedirect('/parsing_in_progress')

      elif not cluster.has_parsed():
         raise cherrypy.HTTPRedirect('/parse_advanced')

      yield self.header()
      yield self.draw_tip("""Click on an event summary to show more details or click <a href="/parse_advanced">here</a> to refine your search.""")

      if cluster.events.count() == 0:
         yield "<i>No events were found.</i>"
         yield self.footer()
         raise StopIteration

      for event in cluster.events.list():

         yield """  <div id="eventPopup">\n"""

#         yield """    <a href="javascript:sndReq('rpc_event_logs','event_id=%d', 'event_logs_%d')"><img src="static/%s">%s</a>\n""" % (event.id, event.id, event.icon, event.text)

         if len(event.logs) == 1:
            yield """    <a href="javascript:load_event_logs(%d);"><img src="static/%s">%s</a>\n""" % (event.id, event.icon, event.text)
         else:
            yield """    <a href="javascript:load_event_logs(%d);"><img src="static/%s" style="position: absolute; left: 32px; z-index: 1"><img src="static/%s">%s</a>\n""" % (event.id, event.icon, event.icon, event.text)

         yield """    <span style="font-size: smaller; color: green; padding-right: 15px"> for %s """ % ( ", ".join(event.affected_hosts()) )

         if len(event.logs) == 1:
            yield """on %s""" % (time.strftime("%A, %d %B %Y at %I:%M:%S %p", event.time_begin()))
         else:
            yield """%d hits starting from %s""" % (len(event.logs), time.strftime("%A, %d %B %Y at %I:%M:%S %p", event.time_begin()))

         yield """</span>"""

         yield """    <div id="event_logs_%d" style='display: none; padding-left: 50px; font: normal normal 9px "Luxi Mono", LucidaTypewriter'></div>""" % event.id

         yield """  </div>\n"""

      yield self.footer()

   def draw_log_and_context(self, log, before = 5, after = 5, highlight = None, show_hostname = False):

      tmplog = log.previous()
      before_logs = []

      while tmplog and before > 0:
         before_logs.insert(0, tmplog)
         tmplog = tmplog.previous()
         before -= 1

      for tmplog in before_logs:
         yield """<div style="color: #9c9c9c">%s</div>\n""" % tmplog.html_print(show_time = True, show_hostname = show_hostname)

      del before_logs

      yield """<div style="color: #9c9c9c">%s</div>\n""" % log.html_print( link_to = self.link_to_log(log), show_time = True, show_hostname = show_hostname, show_events = False)

      while log and after > 0:
         log = log.next()
         yield """<div style="color: #9c9c9c">%s</div>\n""" % log.html_print(show_time = True, show_hostname = show_hostname)
         after -= 1

   def search(self, s = "", x = None, y=None):

      yield self.header()

      # forbidden search terms
      if s in [ "." ]:
         s = ""

      yield """<div style="padding: 30px"><form><input name="s" value="%s"> <input type="image" src="static/images/32px-Crystal_Clear_app_xmag.png"></form>""" % s

      for sterm, hits in cherrypy.session.get('search_history', {}).items():

         yield """<a style="font-size: smaller; color: green; padding-right: 15px" href="/search?s=%s">"%s" (%d results)</a>""" % (sterm, sterm, hits)

      yield """</div>"""

      if len(s):
         yield self.search_results(s)
      else:
         yield """<h3>Please enter a search term (string or regular expression) or click on a previous search.</h3>"""

      yield self.footer()

   def search_results(self, s = ""):

      cluster = self.cluster_master

      search_history = cherrypy.session.get('search_history', {})

      results = cluster.search(s + ".*$")
#      results = cluster.search(s)

      if len(results):
         # store in search_history[search_term]=result_count
         search_history[s]=len(results)

      highl = re.compile("^(.*)(%s)(.*)$" % s, re.IGNORECASE)

      yield """<h3>Search results (%s scanned) - %d hits</h3>""" % (userfriendly_size(cluster.size()), len(results))

      yield html_print_logs(results)

      raise StopIteration

      for log in results:
         yield """<div style='padding-bottom: 20px; font: normal normal 9px "Luxi Mono", LucidaTypewriter'>"""
         yield self.draw_log_and_context(log, show_hostname = True)
         yield """</div>"""

      if len(results) == 0:
         yield """<div style="padding-bottom: 20px">No results.</div>"""

      cherrypy.session['search_history'] = search_history

   def draw_log_rate_graph(self):

      cluster = self.cluster_master

      log_rates = cherrypy.session.get('log_rates', cluster.calculate_log_rate_graph())

      max_height = 50
      max_value = None

      for xdate, rate in log_rates:
         if not max_value or max_value < sum(rate.values()):
            max_value = sum(rate.values())

      yield """<div>"""
      prev_height = None
      multiplier = 1
      for xdate, rate in log_rates:
#         print time.strftime("%B %d %H:%M:%S", xdate), sum(rate.values())
         height = max_height * sum(rate.values()) / max_value
         yield """<div style="background: #595959; width: 2px; height: %dpx; float: right"></div>""" % height
         if prev_height:
            yield """<div style="background: #595959; width: 1px; height: %dpx; float: right"></div>""" % ((prev_height + height) / 2)
         prev_height = height
      yield """</div>"""

      cherrypy.session['log_rates'] = log_rates

   def draw_progressbar(self, percent):
      yield """<div class="progressbar"><div style="width: %d%%;"><p>Parsing in progress (%d%% done)</p></div></div>""" % (percent, percent)

   def draw_tip(self, text, icon = "images/32px-Crystal_Clear_app_ktip.png"):
      yield """<div id="tip"><img src="/static/%s">%s</div>""" % (icon, text)

   def index(self):

      yield self.header()

      cluster = self.cluster_master

      yield """<h3>Welcome to Logst!</h3>"""

      yield """
<div id="page_intro">
You are currently working on <b>%s</b> worth of logs spread across <b>%d</b> files.<p>

You may begin by <a href="/browse">browsing</a> the logs, <a href="/search">searching</a> for a string or regular-expression or looking for known <a href="/events">events</a> based on predefined patterns.
</div>""" % ( userfriendly_size(cluster.size()), len(cluster.get_filenames()) )

      yield """<div>"""
      yield """<table id="widget" class="grid" border="0" cellpadding="0" cellspacing="1">"""

      for host in cluster.list_hosts():
         yield """<thead>"""
         yield """ <tr>"""
         yield """  <th><a href="/host/checksysreport?hostname=%s">%s</a></th>\n""" % (host.hostname(), host.hostname())
         yield """  <th style="min-width: 150px"><a href="#">Size</a></th>\n"""
         yield """  <th style="min-width: 150px"><a href="#">Date start</a></th>\n"""
         yield """  <th style="min-width: 150px"><a href="#">Offset begin (bytes)</a></th>\n"""
         yield """  <th style="min-width: 150px"><a href="#">Offset end (bytes)</a></th>\n"""
         yield """ </tr>\n</thead>\n"""

         yield """<tbody id="logs">\n"""
         sumsize = 0
         for inc in range(0, len(host.logs)):
            if inc % 2 == 0:
               tr_class = "even"
            else:
               tr_class = "odd"

            first_line = host.get_line(sumsize)

            yield """ <tr class="%s">\n""" % tr_class
            yield """  <td><a href="%s">%s</a></td>\n""" % (cherrypy.root.link_to_log(first_line), host.logs[inc].name())
            yield """  <td>%s</td>\n""" % (userfriendly_size(host.logs[inc].size()))
            yield """  <td>%s</td>\n""" % (first_line.syslog_timestamp())
            yield """  <td>%s</td>\n""" % (sumsize)
            yield """  <td>%s</td>\n""" % (sumsize + host.logs[inc].size() - 1)
            yield """ </tr>\n"""

            sumsize += host.logs[inc].size()

         yield """</tbody>\n"""

      yield """</table></div>"""

      yield self.footer()

   def shutdown(self):

      self.shutdown_requested = True

      yield self.header()

      yield "Shutting down..."

      yield self.footer()

      os.kill(os.getpid(), signal.SIGTERM)

   shutdown_requested = False

   index.exposed = True
   exception.exposed = True
   shutdown.exposed = True
   events.exposed = True
   search.exposed = True
   debug.exposed = True
   parse.exposed = True
   rpc_parsing_redirect.exposed = True
   parse_advanced.exposed = True
   parsing_in_progress.exposed = True
   rpc_event_logs.exposed = True
   rpc_star_log.exposed = True
